package com.example.demoeasyexcelboot.listener;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.metadata.holder.ReadHolder;
import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty;
import com.example.demoeasyexcelboot.dto.ExcelErrorDto;
import com.example.demoeasyexcelboot.excel.ExcelCellBo;
import com.example.demoeasyexcelboot.excel.ImportExcelResult;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Path;
import jakarta.validation.Validation;
import jakarta.validation.groups.Default;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author: FSL
 * @date: 2023/4/13
 * @description: TODO
 */
@Slf4j
@Data
public abstract class AbstractDataValidListener<E> extends AnalysisEventListener<E> {
    protected List<E> dataList = new ArrayList<>();
    protected List<ExcelErrorDto> errorList = new ArrayList<>();
    protected ImportExcelResult<E, ExcelErrorDto> result = new ImportExcelResult<>();
    protected List<String> headList = new ArrayList<>();
    protected boolean validate;
    protected Set<String> excelHeadNames = new HashSet<>();

    AbstractDataValidListener(List<String> headList,boolean validate){
        this.headList = headList;
        this.validate = validate;
    }

    AbstractDataValidListener(Class<E> c,boolean validate){
        Field[] declaredFields = c.getDeclaredFields();
        for (Field field : declaredFields){
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (Objects.nonNull(excelProperty)){
                String[] value = excelProperty.value();
                headList.addAll(Arrays.asList(value));
            }
        }
        this.validate = validate;
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        for (Map.Entry<Integer, ReadCellData<?>> dataEntry : headMap.entrySet()) {
            if (! headList.contains(dataEntry.getValue().getStringValue())){
                ExcelErrorDto errorDto = ExcelErrorDto.builder()
                        .headName(dataEntry.getValue().getStringValue())
                        .rowIndex(dataEntry.getValue().getRowIndex())
                        .columnIndex(dataEntry.getValue().getColumnIndex())
                        .errMsg("表头第" + dataEntry.getValue().getRowIndex() + 1 + "行，第" + dataEntry.getValue().getColumnIndex() + 3 + "列格式错误")
                        .build();
                errorList.add(errorDto);
            }
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        result.setData(getDataList());
        result.setError(getErrorList());
        result.setSuccess(getErrorList().isEmpty());
        if (getErrorList().isEmpty() && getDataList().isEmpty()){
            result.setSuccess(false);
            ExcelErrorDto excelErrorDTO = new ExcelErrorDto();
            excelErrorDTO.setErrMsg("excel无数据");
            errorList.add(excelErrorDTO);
        }
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        if (exception instanceof ExcelDataConvertException){
            ExcelDataConvertException e = (ExcelDataConvertException) exception;
            handleExcelDataConvertException(e,context);
        }else {
            throw new RuntimeException(exception.getMessage());
        }
    }

    protected boolean validate(E e, Map<String, ExcelCellBo> propertyNameMap){
        boolean validateResult = true;
        Set<ConstraintViolation<E>> violations = Validation.buildDefaultValidatorFactory().getValidator().validate(e, Default.class);
        if (violations != null && !violations.isEmpty()){
            validateResult = false;
            ExcelErrorDto errorDTO;
            List<ExcelErrorDto> list = new ArrayList<>();
            for (ConstraintViolation<E> constraint : violations){
                Path propertyPath = constraint.getPropertyPath();
                String propertyName = propertyPath.toString();
                ExcelCellBo excelCellBo = propertyNameMap.get(propertyName);
                errorDTO = new ExcelErrorDto();
                errorDTO.setHeadName(excelCellBo.getHeadName());
                Object invalidValue = constraint.getInvalidValue();
                if (Objects.nonNull(invalidValue)){
                    errorDTO.setValue(invalidValue.toString());
                }else {
                    errorDTO.setValue(null);
                }
                errorDTO.setColumnIndex(excelCellBo.getColumnIndex());
                errorDTO.setRowIndex(excelCellBo.getRowIndex());
                errorDTO.setErrMsg("第" + errorDTO.getRowIndex() + 1 + "行第" + errorDTO.getColumnIndex() + 1 + "列，" + constraint.getMessage());
                list.add(errorDTO);
            }
            Collections.sort(list,Comparator.comparingInt(ExcelErrorDto::getRowIndex));
            errorList.addAll(list);
        }
        return validateResult;
    }

    protected void handleExcelDataConvertException(ExcelDataConvertException e,AnalysisContext context){
        Head head = context.currentReadHolder().excelReadHeadProperty().getHeadMap().get(e.getColumnIndex());
        int size = head.getHeadNameList().size();
        ExcelErrorDto errorDTO = ExcelErrorDto.builder()
                .rowIndex(e.getRowIndex())
                .columnIndex(e.getColumnIndex())
                .headName(head.getHeadNameList().get(size - 1))
                .value(e.getCellData().getStringValue())
                .errMsg("第" + e.getRowIndex() + "行第" + e.getColumnIndex() + "列，" + head.getHeadNameList() + "值格式错误")
                .build();
        errorList.add(errorDTO);
    }

    protected Map<String,ExcelCellBo> getPropertyNameMap(boolean isSingleHeader,AnalysisContext context){
        Map<String,ExcelCellBo> propertyNameMap = new HashMap<>();
        ReadRowHolder readRowHolder = context.readRowHolder();
        Integer rowIndex = readRowHolder.getRowIndex();
        ReadHolder readHolder = context.currentReadHolder();
        ExcelReadHeadProperty readHeadProperty = readHolder.excelReadHeadProperty();
        Map<Integer, Head> headMap = readHeadProperty.getHeadMap();
        ExcelCellBo bo;
        for (Map.Entry<Integer, Head> entry : headMap.entrySet()) {
            // 多行表头处理
            bo = ExcelCellBo.builder().headName(String.join(",",entry.getValue().getHeadNameList()))
                    .rowIndex(rowIndex)
                    .columnIndex(entry.getValue().getColumnIndex())
                    .fieldName(entry.getValue().getFieldName())
                    .field(entry.getValue().getField())
                    .build();
            propertyNameMap.put(entry.getValue().getFieldName(),bo);
        }
        return propertyNameMap;
    }
}
